﻿namespace UITest.Selenium.SeleniumContext
{
    using System;
    using System.Drawing.Imaging;
    using System.IO;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using OpenQA.Selenium.Support.Extensions;

    using UITest.Helpers;
    using UITest.Report;

    /// <summary>
    ///     The selenium base page context.
    /// </summary>
    public class SeleniumBasePageContext
    {
        #region Fields

        private readonly Random randomToken = new Random();

        #endregion

        #region Constructors and Destructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="SeleniumBasePageContext" /> class.
        /// </summary>
        public SeleniumBasePageContext()
        {
            this.ProcHelper = new ProcessHelper();
        }

        #endregion

        #region Public Properties

        /// <summary>
        ///     Gets the random token.
        /// </summary>
        public string RandomToken
        {
            get
            {
                return this.GetRandomToken();
            }
        }

        /// <summary>
        ///     Gets the report.
        /// </summary>
        public ReportListener Report
        {
            get
            {
                return ReportFactory.GetReportListener();
            }
        }

        /// <summary>
        ///     Gets the selenium.
        /// </summary>
        public SeleniumContext Selenium
        {
            get
            {
                return SeleniumContextFactory.GetSeleniumContext();
            }
        }

        /// <summary>
        ///     Gets or sets the test context.
        /// </summary>
        public TestContext TestContext { get; set; }

        #endregion

        #region Properties

        private bool HasSelenium
        {
            get
            {
                try
                {
                    if (this.Selenium != null)
                    {
                        return true;
                    }
                }

                    // ReSharper disable once EmptyGeneralCatchClause
                catch (Exception)
                {
                }

                return false;
            }
        }

        private ProcessHelper ProcHelper { get; set; }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The assert that.
        /// </summary>
        /// <param name="istrue">
        /// The istrue.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="repeat">
        /// The repeat.
        /// </param>
        /// <param name="wait">
        /// The wait.
        /// </param>
        public void AssertThat(Func<bool> istrue, string message, int repeat = 3, int wait = 20)
        {
            this.ProcHelper.AssertThat(istrue, message, repeat, wait);
        }

        /// <summary>
        /// The close test case.
        /// </summary>
        /// <param name="testContext">
        /// The test context.
        /// </param>
        public void CloseTestCase(TestContext testContext)
        {
            this.TestContext = testContext;
            this.CloseTestCase();
        }

        /// <summary>
        ///     The close test case.
        /// </summary>
        public void CloseTestCase()
        {
            if (this.Report != null)
            {
                if (this.TestContext.CurrentTestOutcome == UnitTestOutcome.Passed)
                {
                    this.Report.IsPassed = true;
                }

                if (this.HasSelenium)
                {
                    this.FailTestCaseHandler(this.TestContext);
                }

                // var template = new TestCaseDraft(this.Report);
                string path = "C:\\Temp\\testreport.html";
                if (!File.Exists(path))
                {
                    path = string.Format(
                        "{0}\\testreport_{1}.html", 
                        this.TestContext.TestRunResultsDirectory, 
                        this.RandomToken);
                }

                this.TestContext.AddResultFile(path);
            }
        }

        /// <summary>
        /// The fail test case handler.
        /// </summary>
        /// <param name="testContext">
        /// The test context.
        /// </param>
        public void FailTestCaseHandler(TestContext testContext)
        {
            string fileFullPathName = testContext.TestRunResultsDirectory + "/error_"
                                      + DateTime.Now.ToString("yyyyMMddHHmmss") + ".png";

            if (testContext.CurrentTestOutcome == UnitTestOutcome.Failed)
            {
                try
                {
                    this.TakeScreenShotOnTestFail(fileFullPathName);
                    testContext.AddResultFile(fileFullPathName);
                }
                catch (Exception ex)
                {
                    if (this.Report != null)
                    {
                        this.Report.ReportError(ex.ToString());
                    }
                }
            }
        }

        /// <summary>
        /// The new test case.
        /// </summary>
        /// <param name="mstest">
        /// The mstest.
        /// </param>
        /// <param name="bdd">
        /// The bdd.
        /// </param>
        public void NewTestCase(TestContext mstest, bool bdd = true)
        {
            this.TestContext = mstest;
            if (this.HasSelenium)
            {
                this.Selenium.Report = this.Report;
            }
        }

        /// <summary>
        /// The take screen shot on test fail.
        /// </summary>
        /// <param name="fileFullPathName">
        /// The file full path name.
        /// </param>
        public void TakeScreenShotOnTestFail(string fileFullPathName)
        {
            // Capture Selenium Test
            this.Selenium.WebDriver.TakeScreenshot().SaveAsFile(fileFullPathName, ImageFormat.Png);
        }

        #endregion

        #region Methods

        private string GetRandomToken()
        {
            char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            string result = string.Empty;
            int targetBase = chars.Length;
            var epoch = new DateTime(2015 - this.randomToken.Next(0, 100), 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var value = (long)(DateTime.UtcNow - epoch).TotalMilliseconds;

            do
            {
                result = chars[value % targetBase] + result;
                value = value / targetBase;
            }
            while (value > 0);

            return result;
        }

        #endregion
    }
}